home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / glass / glass.lha / GLASS / tm / tmtrans.c < prev    next >
C/C++ Source or Header  |  1990-11-06  |  18KB  |  816 lines

  1. /* 
  2.    Copyright (C) 1990 C van Reewijk, email: dutentb.uucp!reeuwijk
  3.  
  4. This file is part of GLASS.
  5.  
  6. GLASS is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GLASS is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GLASS; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* File: tmtrans.c
  21.  *
  22.  * Translate input file into output file given datastructure
  23.  * description.
  24.  */
  25.  
  26. /* Standard UNIX libraries */
  27. #include <stdio.h>
  28. #include <ctype.h>
  29. #include <tmc.h>
  30.  
  31. /* local definitions */
  32. #include "tmdefs.h"
  33.  
  34. #include "tmds.h"
  35. #include "tmstring.h"
  36. #include "debug.h"
  37. #include "tmfn.h"
  38. #include "tmexpr.h"
  39. #include "tmerror.h"
  40. #include "tmglobal.h"
  41. #include "tmlibpath.h"
  42. #include "tmtplelm.h"
  43. #include "tmmisc.h"
  44. #include "tmtrans.h"
  45. #include "tmvar.h"
  46. #include "tmvers.h"
  47.  
  48. /* mutually recursive local functions */
  49. static void dotrans();
  50.  
  51. /******************************************************
  52.  *                                                    *
  53.  *    Utilities                                       *
  54.  *                                                    *
  55.  ******************************************************/
  56.  
  57. /* tags for command table */
  58. #define EOFLINE 0                 /* special line: end of file */
  59. #define PLAIN 1
  60. #define EXIT 2
  61. #define COMMENT 4
  62. #define IF 5
  63. #define ELSE 6
  64. #define ENDIF 7
  65. #define SET 8
  66. #define ERROR 9
  67. #define INCLUDE 10
  68. #define WHILE 11
  69. #define ENDWHILE 12
  70. #define FOREACH 13
  71. #define ENDFOREACH 14
  72. #define INSERT 15
  73. #define APPEND 16
  74. #define COPY 17
  75.  
  76. /******************************************************
  77.  *                                                    *
  78.  *    Input of template lines                         *
  79.  *                                                    *
  80.  ******************************************************/
  81.  
  82. /* A table of LCOMCHAR commands. The table must be terminated by
  83.    an entry with empty name.
  84.  */
  85. struct dotcom {
  86.     char *dotcomname;
  87.     int dotcomtag;
  88. };
  89.  
  90. struct dotcom dotcomlist[] = {
  91.     { "append", APPEND },
  92.     { "copy", COPY },
  93.     { "else", ELSE },
  94.     { "endforeach", ENDFOREACH },
  95.     { "endif", ENDIF },
  96.     { "endwhile", ENDWHILE },
  97.     { "error", ERROR },
  98.     { "exit", EXIT },
  99.     { "foreach", FOREACH },
  100.     { "if", IF },
  101.     { "include", INCLUDE },
  102.     { "insert", INSERT },
  103.     { "set", SET },
  104.     { "while", WHILE },
  105.     { "", 0 }                       /* end of table mark */
  106. };
  107.  
  108. /* Generate error messages for unbalance in the .<command> and
  109.    .end<command> pairs.
  110.  */
  111. static void unbalance( lno, isterm, needterm )
  112.  int lno;
  113.  int isterm;
  114.  int needterm;
  115. {
  116.     if( isterm == needterm ) return;    /* just for safety */
  117.     if( needterm != EOFLINE ){
  118.     if( isterm != EOFLINE ){
  119.         (void) sprintf( errarg, "unbalanced command at %s(%d)", tplfilename, lno );
  120.         line_error( UNEXPECTDOT );
  121.     }
  122.     else {
  123.         (void) sprintf( errarg, "unterminated command at %s(%d)", tplfilename, lno );
  124.         line_error( UNEXPECTEOF );
  125.     }
  126.     }
  127.     else {
  128.     line_error( EXTRATERM );
  129.     }
  130. }
  131.  
  132. /* Given a file 'f' and a pointer to an int 'endcom', read all lines from
  133.    file 'f' up to the next unbalanced end command and put them in template
  134.    elements. End commands are:
  135.       <eof>
  136.       .else
  137.       .endif
  138.       .endwhile
  139.       .endforeach
  140.  
  141.    Return the list of template elements, and set *endcom to the end command
  142.    that caused termination.
  143.  */
  144. static tplelm readtemplate( f, endcom )
  145.  FILE *f;
  146.  int *endcom;
  147. {
  148.     char *com;
  149.     char *p;
  150.     tplelm head;
  151.     tplelm *link;
  152.     tplelm e1;
  153.     tplelm e2;
  154.     struct dotcom *cp;
  155.     tplelm te;
  156.     int subendcom;
  157.     int firstlno;
  158.     unsigned int inbufsz;
  159.     register unsigned int bufix;
  160.     register char *inbuf;
  161.     register int c;
  162.  
  163.     inbufsz = 100;    /* reasonable initial value */
  164.     inbuf = ckmalloc( inbufsz );
  165.     link = &head;
  166.     for(;;){
  167.     bufix = 0;
  168.     for(;;){
  169.         if( bufix>=inbufsz ){
  170.         inbufsz += inbufsz;
  171.         inbuf = ckrealloc( inbuf, inbufsz );
  172.         }
  173.         c = getc( f );
  174.         if( c == EOF || c == '\n' ){
  175.         inbuf[bufix] = '\0';
  176.         break;
  177.         }
  178.         if( c != '\r' ){
  179.         inbuf[bufix++] = c;
  180.         }
  181.     }
  182.     tpllineno++;
  183.     if( feof( f ) ){
  184.         *link = tplelmNIL;
  185.         *endcom = EOFLINE;
  186.         free( inbuf );
  187.         return( head );
  188.     }
  189.     else if( inbuf[0] == LCOMCHAR ){
  190.         if( inbuf[1] != LCOMCHAR ){
  191.         p = scanword( inbuf+1, &com );
  192.         if( com == CHARNIL ) com = new_string( "" );
  193.         cp = dotcomlist;
  194.         while( strcmp( com, cp->dotcomname ) != 0 ){
  195.             if( cp->dotcomname[0] == '\0' ){
  196.             (void) strcpy( errarg, com );
  197.             line_error( BADDOTCOM );
  198.             exit( 1 );
  199.             }
  200.             cp++;
  201.         }
  202.         fre_string( com );
  203.         switch( cp->dotcomtag ){
  204.             case ENDFOREACH:
  205.             case ELSE:
  206.             case ENDIF:
  207.             case ENDWHILE:
  208.             case EOFLINE:
  209.             *link = tplelmNIL;
  210.             *endcom = cp->dotcomtag;
  211.             free( inbuf );
  212.             return( head );
  213.             
  214.             case IF:
  215.             firstlno = tpllineno;
  216.             e1 = readtemplate( f, &subendcom );
  217.             if( subendcom == ELSE ){
  218.                 e2 = readtemplate( f, &subendcom );
  219.             }
  220.             else {
  221.                 e2 = tplelmNIL;
  222.             }
  223.             if( subendcom != ENDIF ){
  224.                 unbalance( firstlno, subendcom, ENDIF );
  225.             }
  226.             te = new_If( firstlno, new_string( p ), e1, e2 );
  227.             break;
  228.  
  229.             case FOREACH:
  230.             firstlno = tpllineno;
  231.             e1 = readtemplate( f, &subendcom );
  232.             if( subendcom != ENDFOREACH ){
  233.                 unbalance( firstlno, subendcom, ENDFOREACH );
  234.             }
  235.             te = new_Foreach( firstlno, new_string( p ), e1 );
  236.             break;
  237.  
  238.             case WHILE:
  239.             firstlno = tpllineno;
  240.             e1 = readtemplate( f, &subendcom );
  241.             if( subendcom != ENDWHILE ){
  242.                 unbalance( firstlno, subendcom, ENDWHILE );
  243.             }
  244.             te = new_While( firstlno, new_string( p ), e1 );
  245.             break;
  246.  
  247.             case INSERT:
  248.             te = new_Insert( tpllineno, new_string( p ) );
  249.             break;
  250.  
  251.             case INCLUDE:
  252.             te = new_Include( tpllineno, new_string( p ) );
  253.             break;
  254.  
  255.             case COPY:
  256.             te = new_Copy( tpllineno, new_string( p ) );
  257.             break;
  258.  
  259.             case SET:
  260.             te = new_Set( tpllineno, new_string( p ) );
  261.             break;
  262.  
  263.             case APPEND:
  264.             te = new_Append( tpllineno, new_string( p ) );
  265.             break;
  266.  
  267.             case EXIT:
  268.             te = new_Exit( tpllineno, new_string( p ) );
  269.             break;
  270.  
  271.             case ERROR:
  272.             te = new_Error( tpllineno, new_string( p ) );
  273.             break;
  274.         }
  275.         *link = te;
  276.         link = &((*link)->next);
  277.         }
  278.     }
  279.     else {
  280.         *link = new_Plain( tpllineno, new_string( inbuf ) );
  281.         link = &((*link)->next);
  282.     }
  283.     }
  284. }
  285.  
  286. /******************************************************
  287.  *                                                    *
  288.  *    VARCHAR expression evaluation                   *
  289.  *                                                    *
  290.  ******************************************************/
  291.  
  292. /* Given a pointer to an input string 'spi', a pointer to an output string
  293.    'spo', and a stop character 'sc', copy and evaluate the string to the
  294.    stop character 'sc'. Update the position of '*spi' to point to the stop
  295.    character or '\0', and return the evaluated string.
  296.  */
  297. static string alevalto( spi, sc )
  298.  char **spi;
  299.  register char sc;
  300. {
  301.     register string si;
  302.     register string cp;            /* pointer to constructed string */
  303.     unsigned int croom;            /* room in constructed string */
  304.     register unsigned int six;        /* index in constructed string */
  305.     char var1[2];            /* buffer for 1 char variable */
  306.     char *fnval;
  307.     char *v;
  308.     string ans;
  309.     int len;
  310.  
  311.     si = *spi;
  312.     cp = new_string( si );
  313.     croom = strlen( si );
  314.     six = 0;
  315.     if( sevaltr ){
  316.     if( sc == '\0' )
  317.         fprintf( tracestream, "alevalto: '%s'\n", si );
  318.     else
  319.         fprintf( tracestream, "alevalto: '%s' to char '%c'\n", si, sc );
  320.     }
  321.     while( *si != '\0' && *si != sc ){
  322.     /* make sure that 1 character will always fit */
  323.     if( six>=croom ){
  324.         croom += STRSTEP;
  325.         cp = ckrealloc( cp, croom+1 );
  326.     }
  327.     if( *si != VARCHAR ){
  328.         cp[six++] = *si++;
  329.         continue;
  330.     }
  331.     si++;
  332.     if( *si == ORBRAC ){
  333.         si++;
  334.         *spi = si;
  335.         ans = alevalto( spi, CRBRAC );
  336.         si = *spi;
  337.         if( *si != CRBRAC ){
  338.         (void) sprintf( errarg, "'%c'", CRBRAC );
  339.         line_error( NOCLOSEBRAC );
  340.         fre_string( ans );
  341.         continue;
  342.         }
  343.         si++;
  344.         v = getvar( ans );
  345.         fre_string( ans );
  346.         if( v == CHARNIL ){
  347.         (void) strcpy( errarg, ans );
  348.         line_error( VARNOTFOUND );
  349.         continue;
  350.         }
  351.         len = six + strlen( v ) + strlen( si );
  352.         if( len > croom ){
  353.         croom = len;
  354.         cp = ckrealloc( cp, croom+1 );
  355.         }
  356.         while( *v!='\0' ) cp[six++] = *v++;
  357.         continue;
  358.     }
  359.     if( *si == OCBRAC ){
  360.         si++;
  361.         *spi = si;
  362.         ans = alevalto( spi, CCBRAC );
  363.         si = *spi;
  364.         if( *si != CCBRAC ){
  365.         (void) sprintf( errarg, "'%c'", CCBRAC );
  366.         line_error( NOCLOSEBRAC );
  367.         fre_string( ans );
  368.         continue;
  369.         }
  370.         si++;
  371.         fnval = evalfn( ans );
  372.         v = fnval;
  373.         fre_string( ans );
  374.         len = six + strlen( fnval ) + strlen( si );
  375.         if( len > croom ){
  376.         croom = len;
  377.         cp = ckrealloc( cp, croom+1 );
  378.         }
  379.         while( *v!='\0' ) cp[six++] = *v++;
  380.         fre_string( fnval );
  381.         continue;
  382.     }
  383.     if( *si == OSBRAC ){
  384.         si++;
  385.         *spi = si;
  386.         ans = alevalto( spi, CSBRAC );
  387.         si = *spi;
  388.         if( *si != CSBRAC ){
  389.         (void) sprintf( errarg, "'%c'", CSBRAC );
  390.         line_error( NOCLOSEBRAC );
  391.         fre_string( ans );
  392.         continue;
  393.         }
  394.         si++;
  395.         fnval = evalexpr( ans );
  396.         v = fnval;
  397.         fre_string( ans );
  398.         len = six + strlen( fnval ) + strlen( si );
  399.         if( len > croom ){
  400.         croom = len;
  401.         cp = ckrealloc( cp, croom+1 );
  402.         }
  403.         while( *v!='\0' ) cp[six++] = *v++;
  404.         fre_string( fnval );
  405.         continue;
  406.     }
  407.     if( *si == '\0' ){
  408.         cp[six++] = VARCHAR;
  409.         continue;
  410.     }
  411.     if( !isalnum( *si ) ){
  412.         cp[six++] = *si++;
  413.         continue;
  414.     }
  415.     var1[0] = *si++;
  416.     var1[1] = '\0';
  417.     v = getvar( var1 );
  418.     if( v == CHARNIL ){
  419.         (void) strcpy( errarg, var1 );
  420.         line_error( VARNOTFOUND );
  421.         continue;
  422.     }
  423.     len = six + strlen( v ) + strlen( si );
  424.     if( len > croom ){
  425.         croom = len;
  426.         cp = ckrealloc( cp, croom+1 );
  427.     }
  428.     while( *v!='\0' ) cp[six++] = *v++;
  429.     }
  430.     cp[six]='\0';
  431.     *spi = si;
  432.     if( sevaltr ){
  433.     fprintf( tracestream, "value is: '%s'\n", cp );
  434.     }
  435.     return( cp );
  436. }
  437.  
  438. /******************************************************
  439.  *                                                    *
  440.  *    Output file generation                          *
  441.  *                                                    *
  442.  ******************************************************/
  443.  
  444. /* Copy an ordinary line to the output and replace all VARCHAR
  445.    references with the variable.
  446.  */
  447. static void doplain( l, f )
  448.  tplelm l;
  449.  FILE *f;
  450. {
  451.     string is;
  452.     string os;
  453.  
  454.     tpllineno = l->Plain.lno;
  455.     is = l->Plain.plainline;
  456.     os = alevalto( &is, '\0' );
  457.     fputs( os, f );
  458.     fre_string( os );
  459.     putc( '\n', f );
  460. }
  461.  
  462. /* Handle 'copy' command. */
  463. static void docopy( tpl, outfile )
  464.  tplelm tpl;
  465.  FILE *outfile;
  466. {
  467.     string fname;
  468.     FILE *infile;
  469.     char buf[CPBUFSIZE];
  470.     string is;
  471.     string os;
  472.     bool busy;
  473.     register int n;
  474.  
  475.     tpllineno = tpl->Copy.lno;
  476.     is = tpl->Copy.fname;
  477.     os = alevalto( &is, '\0' );
  478.     scan1par( os, &fname );
  479.     fre_string( os );
  480.     if( fname == CHARNIL ) return;
  481.     infile = ckfopen( fname, "r" );
  482.     (void) strcpy( errarg, fname );
  483.     fre_string( fname );
  484.     busy = TRUE;
  485.     do {
  486.     n = fread( buf, sizeof( char ), CPBUFSIZE, infile );
  487.     if( n<=0 ){
  488.         if( ferror( infile ) ){
  489.         sys_error( errno );
  490.         }
  491.         busy = FALSE;
  492.     }
  493.     else {
  494.         n = fwrite( buf, sizeof( char ), n, outfile );
  495.         if( n <= 0 && ferror( outfile ) ){
  496.         sys_error( errno );
  497.         }
  498.     }
  499.     } while( busy );
  500.     fclose( infile );
  501. }
  502.  
  503. /* Handle 'insert' command. */
  504. static void doinsert( tpl, outfile )
  505.  tplelm tpl;
  506.  FILE *outfile;
  507. {
  508.     string fname;
  509.     string oldfname;
  510.     FILE *infile;
  511.     string is;
  512.     string os;
  513.  
  514.     tpllineno = tpl->Insert.lno;
  515.     is = tpl->Insert.fname;
  516.     os = alevalto( &is, '\0' );
  517.     scan1par( os, &fname );
  518.     fre_string( os );
  519.     if( fname == CHARNIL ) return;
  520.     infile = ckfopen( fname, "r" );
  521.     oldfname = tplfilename;
  522.     tplfilename = fname;
  523.     translate( infile, outfile );
  524.     fclose( infile );
  525.     tplfilename = oldfname;
  526.     fre_string( fname );
  527. }
  528.  
  529. /* Handle 'include' command. */
  530. static void doinclude( tpl, outfile )
  531.  tplelm tpl;
  532.  FILE *outfile;
  533. {
  534.     char *fname;
  535.     char *oldfname;
  536.     FILE *infile;
  537.     char *is;
  538.     char *os;
  539.  
  540.     tpllineno = tpl->Include.lno;
  541.     is = tpl->Include.fname;
  542.     os = alevalto( &is, '\0' );
  543.     scan1par( os, &fname );
  544.     fre_string( os );
  545.     if( fname == CHARNIL ) return;
  546.     infile = ckfopen( fname, "r" );
  547.     oldfname = tplfilename;
  548.     tplfilename = fname;
  549.     newvarctx();
  550.     setvar( "templatefile", fname );
  551.     translate( infile, outfile );
  552.     flushvar();
  553.     fclose( infile );
  554.     tplfilename = oldfname;
  555.     fre_string( fname );
  556. }
  557.  
  558. /* Handle 'error' command. */
  559. static void doerror( tpl )
  560.  tplelm tpl;
  561. {
  562.     char *is;
  563.     char *os;
  564.  
  565.     tpllineno = tpl->Error.lno;
  566.     is = tpl->Error.errstr;
  567.     os = alevalto( &is, '\0' );
  568.     (void) fprintf( stderr, "%s\n", os );
  569.     fre_string( os );
  570. }
  571.  
  572. /* Handle 'exit' command. */
  573. static void doexit( tpl )
  574.  tplelm tpl;
  575. {
  576.     char *is;
  577.     char *os;
  578.  
  579.     tpllineno = tpl->Exit.lno;
  580.     is = tpl->Exit.str;
  581.     os = alevalto( &is, '\0' );
  582.     exit( atoi( os ) );
  583.     /* freeing is no use */
  584. }
  585.  
  586. /* Handle 'set' command. */
  587. static void doset( tpl )
  588.  tplelm tpl;
  589. {
  590.     char *is;
  591.     char *os;
  592.     string val;
  593.     string_list sl;
  594.     string_list nl;
  595.     register unsigned int ix;
  596.  
  597.     tpllineno = tpl->Set.lno;
  598.     is = tpl->Set.setline;
  599.     os = alevalto( &is, '\0' );
  600.     sl = chopstring( os );
  601.     fre_string( os );
  602.     if( sl->sz<1 ){
  603.     line_error( NONAME );
  604.     rfre_string_list( sl );
  605.     return;
  606.     }
  607.     nl = new_string_list();
  608.     for( ix=1; ix<sl->sz; ix++ ){
  609.     app_string_list( nl, new_string( sl->arr[ix] ) );
  610.     }
  611.     val = flatstrings( nl );
  612.     rfre_string_list( nl );
  613.     setvar( sl->arr[0], val );
  614.     rfre_string_list( sl );
  615.     fre_string( val );
  616. }
  617.  
  618. /* Handle 'append' command. */
  619. static void doappend( tpl )
  620.  tplelm tpl;
  621. {
  622.     char *is;
  623.     char *os;
  624.     string val;
  625.     string_list sl;
  626.     string_list nl;
  627.     register unsigned int ix;
  628.  
  629.     tpllineno = tpl->Append.lno;
  630.     is = tpl->Append.appline;
  631.     os = alevalto( &is, '\0' );
  632.     sl = chopstring( os );
  633.     fre_string( os );
  634.     if( sl->sz<1 ){
  635.     line_error( NONAME );
  636.     rfre_string_list( sl );
  637.     return;
  638.     }
  639.     nl = new_string_list();
  640.     val = getvar( sl->arr[0] );
  641.     if( val != CHARNIL ){
  642.     app_string_list( nl, new_string( val ) );
  643.     }
  644.     for( ix=1; ix<sl->sz; ix++ ){
  645.     app_string_list( nl, new_string( sl->arr[ix] ) );
  646.     }
  647.     val = flatstrings( nl );
  648.     rfre_string_list( nl );
  649.     setvar( sl->arr[0], val );
  650.     rfre_string_list( sl );
  651.     fre_string( val );
  652. }
  653.  
  654. /* Handle 'if' command. */
  655. static void doif( tpl, outfile )
  656.  tplelm tpl;
  657.  FILE *outfile;
  658. {
  659.     char *is;
  660.     char *os;
  661.     bool cond;
  662.  
  663.     tpllineno = tpl->If.lno;
  664.     is = tpl->If.ifcond;
  665.     os = alevalto( &is, '\0' );
  666.     cond = istruestr( os );
  667.     fre_string( os );
  668.     if( cond ){
  669.     dotrans( tpl->If.ifthen, outfile );
  670.     }
  671.     else {
  672.     dotrans( tpl->If.ifelse, outfile );
  673.     }
  674. }
  675.  
  676. /* Handle 'foreach' command.
  677.    Given a list of template lines, starting with a foreach command line,
  678.    generate output lines. Handle local commands by recursion.
  679.  */
  680. static void doforeach( tpl, outfile )
  681.  tplelm tpl;
  682.  FILE *outfile;
  683. {
  684.     char *nm;
  685.     char *is;
  686.     char *os;
  687.     unsigned int ix;
  688.     string_list sl;
  689.  
  690.     tpllineno = tpl->Foreach.lno;
  691.     is = tpl->Foreach.felist;
  692.     os = alevalto( &is, '\0' );
  693.     sl = chopstring( os );
  694.     fre_string( os );
  695.     if( sl->sz<1 ){
  696.     line_error( NONAME );
  697.     rfre_string_list( sl );
  698.     return;
  699.     }
  700.     nm = sl->arr[0];
  701.     for( ix=1; ix<sl->sz; ix++ ){
  702.     setvar( nm, sl->arr[ix] );
  703.     dotrans( tpl->Foreach.felines, outfile );
  704.     }
  705.     rfre_string_list( sl );
  706. }
  707.  
  708. /* Handle 'while' command.
  709.    Given a list of template lines, starting with a while command line,
  710.    generate output lines until condition is false. Handle local commands
  711.    by recursion.
  712.  */
  713. static void dowhile( tpl, outfile )
  714.  tplelm tpl;
  715.  FILE *outfile;
  716. {
  717.     bool done;
  718.     char *is;
  719.     char *os;
  720.  
  721.     tpllineno = tpl->While.lno;
  722.     while( TRUE ){
  723.     is = tpl->While.whilecond;
  724.     os = alevalto( &is, '\0' );
  725.     done = isfalsestr( os );
  726.     fre_string( os );
  727.     if( done ) return;
  728.     dotrans( tpl->While.whilelines, outfile );
  729.     }
  730. }
  731.  
  732. /* Recursive translation routine.
  733.    Given a list of template lines in 'tpl' generate text from them,
  734.    and write this text to 'outfile'.
  735.  */
  736. static void dotrans( tpl, outfile )
  737.  tplelm tpl;
  738.  FILE *outfile;
  739. {
  740.     while( tpl != tplelmNIL ){
  741.     switch( tpl->tag ){
  742.         case TAGError:
  743.         doerror( tpl );
  744.         break;
  745.  
  746.         case TAGForeach:
  747.         doforeach( tpl, outfile );
  748.         break;
  749.  
  750.         case TAGIf:
  751.         doif( tpl, outfile );
  752.         break;
  753.  
  754.         case TAGPlain:
  755.         doplain( tpl, outfile );
  756.         break;
  757.  
  758.         case TAGSet:
  759.         doset( tpl );
  760.         break;
  761.  
  762.         case TAGAppend:
  763.         doappend( tpl );
  764.         break;
  765.  
  766.         case TAGInclude:
  767.         doinclude( tpl, outfile );
  768.         break;
  769.  
  770.         case TAGInsert:
  771.         doinsert( tpl, outfile );
  772.         break;
  773.  
  774.         case TAGCopy:
  775.         docopy( tpl, outfile );
  776.         break;
  777.  
  778.         case TAGExit:
  779.         doexit( tpl );
  780.         break;
  781.  
  782.         case TAGWhile:
  783.         dowhile( tpl, outfile );
  784.         break;
  785.  
  786.         default:
  787.         (void) sprintf( errarg, "%d", tpl->tag );
  788.         crash( BADTAG );
  789.     }
  790.     tpl = tpl->next;
  791.     }
  792. }
  793.  
  794. /******************************************************
  795.  *                                                    *
  796.  *    Top level of translation routines               *
  797.  *                                                    *
  798.  ******************************************************/
  799.  
  800. void translate( infile, outfile )
  801.  FILE *infile;
  802.  FILE *outfile;
  803. {
  804.     tplelm template;
  805.     int endcom;
  806.  
  807.     tpllineno = 0;
  808.     template = readtemplate( infile, &endcom );
  809.     if( endcom != EOFLINE ){
  810.     unbalance( tpllineno, endcom, EOFLINE );
  811.     return;
  812.     }
  813.     dotrans( template, outfile );
  814.     rfre_tplelm_list( template );
  815. }
  816.